package com.yang.spring.core.bean;

import com.yang.spring.core.bean.builder.*;
import com.yang.spring.core.bean.definition.BeanDefinition;
import com.yang.spring.core.bean.enums.BeanTypeEnum;
import com.yang.spring.core.bean.exception.BeanException;
import com.yang.spring.core.exception.enums.ErrorCode;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class BeanFactory {
    private static Map<BeanTypeEnum, IBeanBuilder> beanType2BeanBuilderMap = new ConcurrentHashMap<>();
    private static Map<BeanDefinition, Object> beanDefinition2BeanMap = new ConcurrentHashMap<>();

    private static Set<BeanDefinition> originBeanDefinitionSet = new HashSet<>();

    static {
        beanType2BeanBuilderMap.put(BeanTypeEnum.COMPONENT, new ComponentBeanBuilder());
        beanType2BeanBuilderMap.put(BeanTypeEnum.SERVICE, new ServiceBeanBuilder());
        beanType2BeanBuilderMap.put(BeanTypeEnum.CONFIGURATION, new ConfigurationBeanBuilder());
        beanType2BeanBuilderMap.put(BeanTypeEnum.BEAN, new BeanBeanBuilder());
    }

    public static void putBean(BeanDefinition beanDefinition, Object bean) {
        beanDefinition2BeanMap.put(beanDefinition, bean);
    }

    public static Object getBeanByName(BeanDefinition beanDefinition) {
        BeanDefinition originBeanDefinition = getBeanDefinitionByName(beanDefinition.getBeanName(), beanDefinition.getBeanClassType());
        return beanDefinition2BeanMap.get(originBeanDefinition);
    }

    public static Object getBeanByType(Class clazz) {
        List<Object> beanList = getBeansByType(clazz);
        if (beanList.size() > 1) {
            throw new BeanException(ErrorCode.MORE_THAN_ONE_BEAN_OF_THIS_TYPE, clazz.getName());
        } else if (beanList.isEmpty()) {
            throw new BeanException(ErrorCode.BEAN_TYPE_NOT_EXIST, clazz.getName());
        }
        return beanList.get(0);
    }

    public static List<Object> getBeansByType(Class clazz) {
        List<Object> beanList = new ArrayList<>();
        for (Object bean : beanDefinition2BeanMap.values()) {
            if (clazz.isAssignableFrom(bean.getClass())) {
                beanList.add(bean);
            }
        }
        return beanList;
    }

    public static Object getBeanByName(String beanName, Class clazz) {
        BeanDefinition beanDefinition = getBeanDefinitionByName(beanName, clazz);
        if (beanDefinition == null) {
            return null;
        }
        return beanDefinition2BeanMap.get(beanDefinition);
    }

    public static BeanDefinition getBeanDefinitionByName(String beanName, Class clazz) {
        BeanDefinition beanDefinition = null;
        for (BeanDefinition definition : originBeanDefinitionSet) {
            if (definition.getBeanName().equals(beanName)
                    && clazz.isAssignableFrom(definition.getBeanClassType())) {
                beanDefinition = definition;
                break;
            }
        }
        return beanDefinition;
    }

    public static Map<String, Object> getClassName2BeanMap() {
        Map<String, Object> className2BeanMap = new HashMap<>();
        beanDefinition2BeanMap.forEach(((beanDefinition, o) -> {
            className2BeanMap.put(beanDefinition.getBeanName(), o);
        }));
        return className2BeanMap;
    }

    public static IBeanBuilder getBeanBuilderByBeanType(BeanTypeEnum beanTypeEnum) {
        return beanType2BeanBuilderMap.get(beanTypeEnum);
    }

    public static void initBeans(List<BeanDefinition> beanDefinitionList) {
        originBeanDefinitionSet.addAll(beanDefinitionList);
        try {
            for (BeanDefinition beanDefinition : beanDefinitionList) {
                BeanDefinition targetDefinition = null;
                for (BeanDefinition definition : beanDefinition2BeanMap.keySet()) {
                    if (beanDefinition.getBeanClassType().equals(definition.getBeanClassType())
                    && beanDefinition.getBeanName().equals(definition.getBeanName())) {
                        targetDefinition = definition;
                        break;
                    }
                }
                if (targetDefinition != null && targetDefinition.haveInstance()) {
                    beanDefinition2BeanMap.remove(targetDefinition);
                    beanDefinition2BeanMap.put(beanDefinition, targetDefinition.getInstance());
                    continue;
                }
                buildBeanInstance(beanDefinition);
            }
        }  catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static Object buildBeanInstance(BeanDefinition beanDefinition) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        BeanDefinition originBeanDefinition = getBeanDefinitionByName(beanDefinition.getBeanName(), beanDefinition.getBeanClassType());
        BeanTypeEnum beanType = originBeanDefinition.getBeanType();
        IBeanBuilder iBeanBuilder = beanType2BeanBuilderMap.get(beanType);
        long startTime = System.currentTimeMillis();
        Object instance = iBeanBuilder.buildBean(originBeanDefinition);
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("类型为%s 的%s 构建完成，耗时:%s",
                beanDefinition.getBeanClassType(),
                beanDefinition.getBeanName(),
                (endTime - startTime)));
        return instance;
    }
}
